//
//
//
//
//
//
//
#include <unistd.h>
#include <stdarg.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>

#include "File.h"
#include "Exception.h"
#include "StrStream.h"

#define MAX_APPEND_BUF_SIZE (1024)


const char* File::ENDL = "\n";

File::File(const std::string& path) :
	_fd(-1),
	_isOpen(false),
	_path(path){};

File::File(void) : _fd(-1), _isOpen(false){};

File::~File()
{
	if(_isOpen)
		Close();
}

//
//
// Open metherd
void File::Open(U32 flag, U32 mode)
{
	if(_isOpen)
		return;
	Open(_path, flag, mode);
}

//
//
// Open
void File::Open(const std::string& path, U32 flag, U32 mode)
{
	Close();
	_fd = open(path.c_str(), flag, mode);
	if(_fd == -1)
	{
		Util::StrStream str;
		str << "Error Open file:" << path
			<< ", " << strerror(errno);
		throw Exception(str);
	}
	_path = path;
	_isOpen = true;
}

void File::Close(void)
{
	if(_isOpen)
	{
		close(_fd);
		_isOpen = false;
	}
}

ssize_t File::Append(const char* fmt, ...)
{
	if(!_isOpen)
		throw Exception("Call Open methord first");
	char szBuf[MAX_APPEND_BUF_SIZE];
	va_list args;
	va_start(args, fmt);

	ssize_t bytes;
	vsprintf(szBuf, fmt, args);
	bytes = write(_fd, szBuf, strlen(szBuf));
	if(bytes == -1)
	{
		Util::StrStream str;
		str << "Error write file" << _path
			<< ", " << strerror(errno);
		throw Exception(str);
	}
	return bytes;
}

void File::Remove()
{
	Close();
	if(remove(_path.c_str()) != 0 )
	{
		Util::StrStream str;
		str << "Error remove file" << _path
			<< ", " << strerror(errno);
		throw Exception(str);
	}
}

void File::Unlink(const std::string& path)
{
	if(unlink(_path.c_str()) == -1)
	{
		Util::StrStream str;
		str << "Error unlink file" << _path
			<< ", " << strerror(errno);
		throw Exception(str);

	}
}
void File::Unlink()
{
	if(_isOpen)
		close(_fd);
	if(unlink(_path.c_str()) == -1)
	{
		Util::StrStream str;
		str << "Error unlink file" << _path
			<< ", " << strerror(errno);
		throw Exception(str);
	}
}

bool File::IsOpen(void){ return _isOpen;}

void File::Rename(const std::string& src, const std::string& dest)
{
	if(-1 == rename(src.c_str(), dest.c_str()))
	{
		Util::StrStream str;
		str << "Error rename file: "
			<< src << " to " << dest
			<< ", " << strerror(errno);
		throw Exception(str);
	}
}

//
//
//
std::string File::GetSpaceN(U32 count)
{
	if(count >= 1024)
		throw Exception("1024 space too large");
	char szBuf[1024];
	memset(szBuf, 0x20, sizeof(szBuf));
	szBuf[count] = '\0';
	return std::string(szBuf);
}


//
//
//
std::string File::Path(void) const
{
	return _path;
}

void File::Flush(void)
{
	;
}

void File::MapToMem(void*& addr /*OUT*/, size_t len, int prot, int flag, off_t off)
{
	addr = mmap(0, len, prot, flag, _fd, off);
	if((void*)-1 == addr)
	{
		Util::StrStream str;
		str << "Error at File::MapToMem, path : "
			<<  _path
			<< ", " << strerror(errno);
		throw Exception(str);
	}
}

void File::Stat(struct stat& fill)
{
	if(!IsOpen())
		throw Exception("File::Open should be called first");
	if(-1 == fstat(_fd, &fill))
	{
		Util::StrStream str;
		str << "Error at File::Stat, path : "
			<< _path
			<< ", " << strerror(errno);
		throw Exception(str);
	}
}


void File::Stat(const std::string& path, struct stat& fill)
{
	memset(&fill, 0, sizeof(struct stat));
	if(-1 == stat(path.c_str(), &fill))
	{
		Util::StrStream str;
		str << "Error at File::Stat, path : "
			<< _path
			<< ", " << strerror(errno);
		throw Exception(str);
	}

}

File& operator << (File& file, const std::string& msg)
{
	if(!file.IsOpen())
		throw Exception("File should be opened before call operator <<");
	file.Append(msg.c_str());
	return file;
}
